/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#ifndef __VM_H__
#define __VM_H__

#include <meta.h>       /* C语言相关的类型定义 */
#include <arch/vm.h>

// #include <platform.h>
#include <mem.h>
#include <cpu.h>
#include <spinlock.h>
#include <config.h>
#include <emul.h>
#include <objcache.h>
#include <interrupts.h>
#include <bitmap.h>
#include <iommu.h>
#include <ipc.h>

/** 表示虚拟机的数据结构
 * 
 * 1. 为了标识虚拟机，需要记录每个虚拟机的ID
 * 2. 每个虚拟机需要一个主核 master
 * 3. 为虚拟机模拟CPU核，并记录Core数量。（这儿使用一个list列表表示）
 * 4. 每个虚拟机具有自己的地址空间
 * 5. 每个虚拟机中跟模拟架构相关的数据结构
 * 6. TODO：模拟设备
 * 7. 每个虚拟机支持的中断（这儿使用位图的方式支持）
 * 8. 虚拟机之间通信IPC
 * 9. 对数据的保护，需要spinlock和同步原语
 */
struct vm {
    vmid_t                  id;                /* 虚拟机ID */

    const struct vm_config* config;

    spinlock_t              lock;
    struct cpu_synctoken    sync;
    cpuid_t                 master;

    struct list             vcpu_list;
    size_t                  cpu_num;
    cpumap_t                cpus;

    struct addr_space       as;

    struct vm_arch          arch;

    struct list             emul_list;
    struct objcache         emul_oc;

    struct iommu_vm         iommu;

    BITMAP_ALLOC(interrupt_bitmap, MAX_INTERRUPTS);

    size_t                  ipc_num;
    struct ipc*             ipcs;
};

/** 
 * 表示虚拟CPU核（vCPU）的数据结构
 */
struct vcpu {
    /* 1. 链表节点元素，用于串联虚拟机的vCPU */
    node_t              node;
    /* 2. vCPU虚拟核上下文：通用寄存器 */
    struct arch_regs*   regs;
    /* 3. vCPU虚拟核上下文：架构相关的内容 - 虚拟机的中断、启动入口 */
    struct vcpu_arch    arch;
    /* 4. 与物理核的关系 */
    vcpuid_t            id;
    cpuid_t             phys_id;
    bool                active;
    /* 5. 运行在该虚拟核的VM数据结构 */
    struct vm*          vm;
    /* 6. vCPU虚拟核上下文：堆栈 */
    uint8_t stack[STACK_SIZE] __attribute__((aligned(STACK_SIZE)));
};

extern struct vm vm;
extern struct config* vm_config_ptr;

void vm_init(struct vm* vm, const struct vm_config* config, bool master, vmid_t vm_id);
// void vm_start(struct vm* vm, vaddr_t entry);

void vm_emul_add_mem(struct vm* vm, struct emul_mem* emu);
void vm_emul_add_reg(struct vm* vm, struct emul_reg* emu);
emul_handler_t vm_emul_get_mem(struct vm* vm, vaddr_t addr);
emul_handler_t vm_emul_get_reg(struct vm* vm, vaddr_t addr);
// void vcpu_init(vcpu_t* vcpu, struct vm* vm, vaddr_t entry);
void vm_msg_broadcast(struct vm* vm, struct cpu_msg* msg);
cpumap_t vm_translate_to_pcpu_mask(struct vm* vm, cpumap_t mask, size_t len);
cpumap_t vm_translate_to_vcpu_mask(struct vm* vm, cpumap_t mask, size_t len);

/* vcpu_id -> pcpu_id */
static inline cpuid_t vm_translate_to_pcpuid(struct vm* vm, vcpuid_t vcpuid)
{
    ssize_t i = bitmap_find_nth((bitmap_t*)&vm->cpus, sizeof(vm->cpus) * 8,
                           vcpuid + 1, 0, true);
    if(i < 0) {
        return INVALID_CPUID;
    } else {
        return (cpuid_t)i;
    }
}

static inline vcpuid_t vm_translate_to_vcpuid(struct vm* vm, cpuid_t pcpuid)
{
    if (vm->cpus & (1UL << pcpuid)) {
        return (cpuid_t)bitmap_count((bitmap_t*)&vm->cpus, 0, pcpuid, true);
    } else {
        return INVALID_CPUID;
    }
}

static inline bool vm_has_interrupt(struct vm* vm, irqid_t int_id)
{
    return !!bitmap_get(vm->interrupt_bitmap, int_id);
}

/****************************************************************************
 ****                         公共函数接口                               ****
 ****************************************************************************/

void            vm_arch_init(struct vm* vm, const struct vm_config* config);
struct vcpu*    vm_get_vcpu(struct vm* vm, vcpuid_t vcpuid);
void            vcpu_arch_init(struct vcpu* vcpu, struct vm* vm);
void            vcpu_arch_reset(struct vcpu* vcpu, vaddr_t entry);
void            vcpu_arch_run(struct vcpu* vcpu);
unsigned long   vcpu_readreg(struct vcpu* vcpu,
                              unsigned long reg);
void            vcpu_writereg(struct vcpu* vcpu,
                              unsigned long reg,
                              unsigned long val);
void            vcpu_run(struct vcpu* vcpu);



unsigned long   vcpu_readpc(struct vcpu* vcpu);
void            vcpu_writepc(struct vcpu* vcpu, unsigned long pc);


#endif /* __VM_H__ */